#include "common.h"
#include "sock.h"
#include "sock_kqueue.h"
#include "server.h"

#if 0
#define KE_QUEUE_STEP 1024
#define STATE_READ      1
#define STATE_WRITE     2

static void kqueue_event_changelist_set(event_t *ev, int fd, int need_read, int need_write);
static void kqueue_event_callback(event_t *ev, int flag);

int kqueue_event_set(event_t *ev)
{
	int res = 0;
	int need_read = 0;
	int need_write = 0;

	if(!ev)
		return -1;
	
	if(ev->flag == SOCK_FLAG_TIMEOUT) {
		return res;
	}
	else if(ev->flag == SOCK_FLAG_CLOSE) 
		return res;
	if(ev->flag == SOCK_FLAG_READ) {
		need_read = 1;
	}
	if(ev->flag == SOCK_FLAG_WRITE) 
		need_write = 1;
	
	kqueue_event_changelist_set(ev, ev->fd, need_read, need_write);
	return res;
}

event_base_t * kqueue_event_base_new()
{
	event_base_t *base;

	if((base = mempool_alloc(sizeof(event_base_t))) == NULL) {
		error_log(errno, "failed to alloc %d bytes\n", sizeof(event_base_t));
		return NULL;
	}
	base->total = 0;
	base->active = 0;
	base->kq = kqueue();
	if(base->kq == -1) {
		server_down(0);
		return NULL;
	}
	base->kqmax = KE_QUEUE_STEP;
	base->kqoff = 0;
	base->kqlst = mempool_alloc(base->kqmax * sizeof(*base->kqlst));
	base->ke = mempool_alloc(base->kqmax * sizeof(*base->ke));
	base->kqueue_state = mempool_alloc(conf->server.maxfds);
	INIT_LIST_HEAD(&base->active_list);
	INIT_LIST_HEAD(&base->free_list);
	return base;
}

void kqueue_event_base_run(event_base_t *base)
{
	int i;
	int num;
	struct timespec timeout = {0, 100};

	assert(base);
	if(base->stop)
		return;
	num = kevent(base->kq, base->kqlst, base->kqoff, base->ke, base->kqmax, &timeout);
	base->kqoff = 0;
	if (num < 0) {
		if (error_ignore(errno))
			return;

		error_log(errno, "%s failure\n", "kevent");
		return;
	}
	if(num == 0) 
		return;
	debug_log(5, "%d fds ready\n", num);
	for (i = 0; i < num; i++) {
		event_t *ev = base->ke[i].udata;
		int fd = (int) base->ke[i].ident;

		if (base->ke[i].flags == EV_ERROR) {
			errno = base->ke[i].data;
			error_log(errno, "errno %d\n", errno);
			continue;
		}
		switch (base->ke[i].filter) {
			case EVFILT_READ:
				kqueue_event_callback(ev, base->ke[i].filter);
				break;
			case EVFILT_WRITE:
				kqueue_event_callback(ev, base->ke[i].filter);
				break;
			default:
				debug_log(5, "unexpected event: %d, fd %d\n", base->ke[i].filter, fd);
				break;
		}
	}

	if (num >= base->kqmax) {
		base->kqmax = base->kqmax + KE_QUEUE_STEP;
		base->kqlst = mempool_realloc(base->kqlst, sizeof(*base->kqlst) * base->kqmax);
		base->ke = mempool_realloc(base->ke, sizeof(*base->ke) * base->kqmax);
	}
}

static void kqueue_event_changelist_set(event_t *ev, int fd, int need_read, int need_write)
{
	if(!ev)
		return;

	event_base_t *base = ev->base;
	struct kevent *kep;
	int st_new = (need_read ? STATE_READ : 0) |
				(need_write ? STATE_WRITE : 0);
	int st_change;

	if(fd < 0) {
		debug_log(1, "Warning! fd %d < 0\n", fd);
		return;
	}
	assert(base);

	st_change = base->kqueue_state[fd] ^ st_new;
	if (!st_change) {
		debug_log(9, "fd %d state not change\n", fd);
		return;
	}

	if (base->kqoff >= base->kqmax - 2) {
		base->kqmax = base->kqmax + KE_QUEUE_STEP;
		assert(base->kqmax < conf->server.maxfds * 4);
		base->kqlst = mempool_realloc(base->kqlst, sizeof(*base->kqlst) * base->kqmax);
		base->ke = mempool_realloc(base->ke, sizeof(*base->ke) * base->kqmax);
	}
	kep = base->kqlst + base->kqoff;

	if (st_change & STATE_READ) {
			EV_SET(kep, (uintptr_t) fd, EVFILT_READ,
								need_read ? (EV_ADD | EV_ENABLE) : EV_DELETE, 0, 0, ev);
			base->kqoff++;
			kep++;
	}
	if (st_change & STATE_WRITE) {
			EV_SET(kep, (uintptr_t) fd, EVFILT_WRITE,
								need_write ? (EV_ADD | EV_ENABLE) : EV_DELETE, 0, 0, ev);
			base->kqoff++;
			kep++;
	}
	base->kqueue_state[fd] = st_new;
	debug_log(5, "fd=%d, read=%d, write=%d, kqoff %d\n", \
					fd, need_read, need_write, base->kqoff);
}

static void kqueue_event_callback(event_t *ev, int flag)
{
	if(!ev)
		return;
	void (*cb) (int, short, void *);
	void *data = NULL;

	cb = NULL;
	if(flag == EVFILT_WRITE) {
		cb = ev->write_cb;
		data = ev->write_data;
	}
	else if(flag == EVFILT_READ) {
		cb = ev->read_cb;
		data = ev->read_data;
	}

	if(!cb)
		return;

	cb(ev->fd, ev->flag, data);
}

void kqueue_event_base_stop(event_base_t *base)
{
	debug_log(1, "kqueue %d break\n", base->kq);
	base->stop = 1;
}

void kqueue_event_base_clean(event_base_t *base)
{
	if(!base)
		return;

	sock_close(base->kq);
	mempool_free(base->kqlst);
	mempool_free(base->ke);
	mempool_free(base->kqueue_state);
	mempool_free(base);
}
#endif
